package com.anlogic.sdk.ui;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.actions.CloseResourceAction;
import org.eclipse.ui.actions.CloseUnrelatedProjectsAction;
import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.navigator.CommonActionProvider;
import org.eclipse.ui.navigator.ICommonActionExtensionSite;
import org.eclipse.ui.navigator.ICommonMenuConstants;

public class CustomResourceMgmtActions extends CommonActionProvider {
	private CustomCloseResourceAction closeProjectAction;
	private CustomCloseUnrelatedProjectsAction closeUnrelatedProjectsAction;
	
	private Shell shell;

	@Override
	public void init(ICommonActionExtensionSite aSite) {
		super.init(aSite);
		shell = aSite.getViewSite().getShell();
		makeActions();
	}
	
	@Override
	public void fillActionBars(IActionBars actionBars) {
		actionBars.setGlobalActionHandler(IDEActionFactory.CLOSE_PROJECT.getId(), closeProjectAction);
		actionBars.setGlobalActionHandler(IDEActionFactory.CLOSE_UNRELATED_PROJECTS.getId(), closeUnrelatedProjectsAction);
		updateActionBars();
	}
	
	@Override
	public void fillContextMenu(IMenuManager menu) {
		IStructuredSelection selection = (IStructuredSelection) getContext().getSelection();
		boolean hasOpenProjects = false;
		boolean hasClosedProjects = false;
		boolean hasBuilder = true; // false if any project is closed or does not have builder
		List<IProject> projects = selectionToProjects(selection);

		for (IProject project : projects) {
			if (hasOpenProjects && hasClosedProjects && !hasBuilder) {
				// we've set all booleans of interest; no need to loop any further
				break;
			}
			if (project.isOpen()) {
				hasOpenProjects = true;
				if (hasBuilder && !hasBuilder(project)) {
					hasBuilder = false;
				}
			} else {
				hasClosedProjects = true;
				hasBuilder = false;
			}
		}
		
		if (hasOpenProjects) {
			IContributionItem oldClose = menu.find(CloseResourceAction.ID);
			if (oldClose != null) {
				menu.remove(oldClose);
			}
			IContributionItem oldCloseUnrelated = menu.find(CloseUnrelatedProjectsAction.ID);
			if (oldCloseUnrelated != null) {
				menu.remove(oldCloseUnrelated);
			}
			closeProjectAction.selectionChanged(selection);
			menu.appendToGroup(ICommonMenuConstants.GROUP_BUILD, closeProjectAction);
			closeUnrelatedProjectsAction.selectionChanged(selection);
			menu.appendToGroup(ICommonMenuConstants.GROUP_BUILD, closeUnrelatedProjectsAction);
		}
	}
	
	private static List<IProject> selectionToProjects(IStructuredSelection selection) {
		if (selection == null) {
			return Collections.emptyList();
		}
		List<IProject> resources = new ArrayList<>();
		for (Object currentObject : selection) {
			if (currentObject instanceof IWorkingSet) {
				IWorkingSet workingSet = (IWorkingSet) currentObject;
				for (IAdaptable element : workingSet.getElements()) {
					IProject project = element.getAdapter(IProject.class);
					if (project != null) {
						resources.add(project);
					}
				}
			} else if (currentObject instanceof IAdaptable) {
				IProject resource = ((IAdaptable) currentObject).getAdapter(IProject.class);
				if (resource != null) {
					resources.add(resource);
				}
			}
		}
		return resources;
	}

	/**
	 * Returns whether there are builders configured on the given project.
	 *
	 * @return <code>true</code> if it has builders, <code>false</code> if not,
	 *         or if this could not be determined
	 */
	boolean hasBuilder(IProject project) {
		try {
			ICommand[] commands = project.getDescription().getBuildSpec();
			if (commands.length > 0) {
				return true;
			}
		} catch (CoreException e) {
			// Cannot determine if project has builders. Project is closed
			// or does not exist. Fall through to return false.
		}
		return false;
	}
	
	protected void makeActions() {
		IShellProvider sp = () -> shell;
		closeProjectAction = new CustomCloseResourceAction(sp);
		closeUnrelatedProjectsAction = new CustomCloseUnrelatedProjectsAction(sp);
	}
	
	@Override
	public void updateActionBars() {
		IStructuredSelection selection = (IStructuredSelection) getContext().getSelection();
		closeUnrelatedProjectsAction.selectionChanged(selection);
		closeProjectAction.selectionChanged(selection);
	}
}
